/*================================================================
 *  xpm2s.c
 *               Convert an XPM picture to assembly source
 *
 *================================================================
 *
 * 25thanni, a demo dedicated to the 25th anniversary of the ZX81.
 *
 * (c)2006 Bodo Wenzel
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the Free
 * Software Foundation Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *================================================================
 */

/* This is a classical filter, reading from stdin and writing to
 * stdout. The input is an XPM file, the output is ment to be
 * included into an ASxxxx source.
 */

/* Externals ================================================== */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include "pack.h"

/* Constants ================================================== */

#define LINE_LEN      5000
#define MAX_COLOR     5
#define UNPACKED_SIZE 8192
#define PACKED_SIZE   8192

/* Variables ================================================== */

static size_t        unpacked_len;
static unsigned char unpacked[UNPACKED_SIZE];
static size_t        packed_len;
static unsigned char packed[PACKED_SIZE];

/* Prototypes ================================================= */

static void
quit(const char *message);

static void
getline(int fields, const char *format, ...);

static void
generate(int pixel[], int height, int width, char color[],
	 int plane);

/* Program code =============================================== */

/* Main function ---------------------------------------------- */

int main(void) {
  char dummy[LINE_LEN];
  char line[LINE_LEN];
  int  width, height, colors, chars;
  int  i, j, k;
  char color[MAX_COLOR], hex[LINE_LEN];
  int  rgb[MAX_COLOR];
  int  *pixel;

  /* read picture---------------------------------------------- */

  getline(4, "\"%d%d%d%d\"", &width, &height, &colors, &chars);
  if (colors > MAX_COLOR) {
    quit("Too many colors");
  }
  for (i = 0; i < colors; i++) {
    getline(3, "\"%c%s%s\"", &color[i], dummy, hex);
    if (sscanf(hex, "#%x\"", &rgb[i]) != 1) {
      color[i] = '\0';
      rgb[i] = -1;
    }
  }
  pixel = malloc(height * width * sizeof(int));
  if (pixel == NULL) {
    quit("Can't allocate memory");
  }
  for (i = 0; i < height; i++) {
    getline(1, "\"%s", line);
    if (strlen(line) <= (size_t)width) {
      quit("Premature EOF");
    }
    if (line[width] != '\"') {
      quit("Format error");
    }
    for (j = 0; j < width; j++) {
      for (k = 0; k < colors; k++) {
	if (line[j] == color[k]) {
	  pixel[i * width + j] = k;
	  break;
	}
      }
      if (k >= colors) {
	quit("Color not found");
      }
    }
  }

  /* assign value to colors ----------------------------------- */

  for (i = 0; i < colors; i++) {
    if (rgb[i] == -1) {
      color[i] = -1;
    } else if (rgb[i] == 0xFFFFFF) {
      color[i] = 0;
    } else if (rgb[i] == 0x000000) {
      color[i] = 1;
    } else if (rgb[i] == 0x7F7F7F || rgb[i] == 0x808080) {
      color[i] = 2;
    } else if (rgb[i] == 0x555555) {
      color[i] = 3;
    } else if (rgb[i] == 0xAAAAAA) {
      color[i] = 4;
    } else {
      quit("Color not supported");
    }
  }

  /* generate all pictures ------------------------------------ */

  for (i = 0; i < height; i++) {
    for (j = 0; j < width; j++) {
      if (color[pixel[i * width + j]] == -1) {
	quit("Transparency not supported");
      }
    }
  }
  for (i = 0; i < colors - 2; i++) {
    generate(pixel, height, width, color, i);
  }

  /* pack and save all data ----------------------------------- */

  packed_len = pac(PACKED_SIZE, packed, unpacked_len, unpacked);
  if (packed_len == 0) {
    quit("Buffer overflow in packed data");
  }
  printf("UNPACKED\t=\t%u\n", unpacked_len);
  for (i = 0; i < (int)packed_len; i++) {
    printf("\t.db\t0x%02X\n", packed[i]);
  }

  return 0;
}

/* Report a message and exit the program ---------------------- */

static void
quit(const char *message) {
  fputs(message, stderr);
  fputs("!\n", stderr);
  exit(EXIT_FAILURE);
}

/* Get a line of input and decode it -------------------------- */

static void
getline(int fields, const char *format, ...) {
  char    line[LINE_LEN];
  va_list ap;

  va_start(ap, format);

  for (;;) {
    if (fgets(line, LINE_LEN, stdin) == NULL) {
      quit("Premature EOF");
    }

    if (vsscanf(line, format, ap) == fields) {
      break;
    }
  }

  va_end(ap);
}

/* Generate a picture ----------------------------------------- */

static void
generate(int pixel[], int height, int width, char color[],
	 int plane) {
  int i, j, b;

  for (i = 0; i < height; i++) {
    b = 1;
    for (j = 0; j < width; j++) {
      b <<= 1;
      switch (color[pixel[i * width + j]]) {
      case 1: /* black */
	b |= 1;
	break;
      case 2: /* grey */
	if ((i + j) % 2 == plane) {
	  b |= 1;
	}
	break;
#if 1 /* vertical runs */
      case 3: /* dark grey */
	if ((i + j % 2) % 3 != plane) {
	  b |= 1;
	}
	break;
      case 4: /* light grey */
	if ((i + j % 2) % 3 == plane) {
	  b |= 1;
	}
	break;
#else /* horizontal runs */
      case 3: /* dark grey */
	switch (plane) {
	case 1:
	  if ((i + j) % 3 != 1 + ((i / 2) & 1)) {
	    b |= 1;
	  }
	  break;
	case 2:
	  if ((i + j) % 3 != 2 - ((i / 2) & 1)) {
	    b |= 1;
	  }
	  break;
	default:
	  if ((i + j) % 3 != 0) {
	    b |= 1;
	  }
	  break;
	}
	break;
      case 4: /* light grey */
	switch (plane) {
	case 1:
	  if ((i + j) % 3 == 1 + ((i / 2) & 1)) {
	    b |= 1;
	  }
	  break;
	case 2:
	  if ((i + j) % 3 == 2 - ((i / 2) & 1)) {
	    b |= 1;
	  }
	  break;
	default:
	  if ((i + j) % 3 == 0) {
	    b |= 1;
	  }
	  break;
	}
	break;
#endif
      default:
	break;
      }
      if (b > 0xFF) {
	unpacked[unpacked_len++] = b & 0xFF;
	if (unpacked_len > UNPACKED_SIZE) {
	  quit("Buffer overflow in unpacked data");
	}
	b = 1;
      }
    }
  }
}

/* The end ==================================================== */
